/* 
 * Copyright (c) 2009, Giora Kosoi giorak@gmail.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Giora Kosoi ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Giora Kosoi BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


#include <stdio.h>
#include <assert.h>

#include <sparrow/http_parser.h>
#include <sparrow/http_header_variant_set.h>
#include <sparrow/variant_headers.h>
#include <sparrow/util.h>

#include "test_helpers.h"

void test()
{
    Octets request;
    join(request, "\r\n",
        "GET http://duke:ofprunes@upload.wikimedia.org:80/wikipedia/commons/3/33/House_sparrowIII.jpg?query#fragment HTTP/1.1",
        "User-Agent: Opera/9.60 (Windows NT 5.1; U; en) Presto/2.1.1",
        "Host: upload.wikimedia.org",
        "Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1",
        "Accept-Language: en-US,en;q=0.9",
        "Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1",
        "Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0",
        "Referer: http://en.wikipedia.org/wiki/File:House_sparrowIII.jpg",
        "Pragma: no-cache",
        "Cache-Control: no-cache",
        "Connection: Keep-Alive, TE",
        "TE: deflate, gzip, chunked, identity, trailers",
        "",
        "",
        NULL);

           
    Octets reminder;
    HttpHeaderVariantSet header_collector;
    HttpParser parser(&header_collector);
    bool request_complete = parser.parse_request(request, reminder);
    assert(request_complete == true);

    VariantSet *http_header = header_collector.release_header();

    // reguest line
    // method
    assert(http_header->get("method")->str() == "GET");

    // request uri
    VariantSet *uri = http_header->get("request-uri")->variant_set();
    assert(uri->get("request-uri")->str() == "http://duke:ofprunes@upload.wikimedia.org:80/wikipedia/commons/3/33/House_sparrowIII.jpg?query#fragment");
    assert(uri->get("schema")->str() == "http");
    assert(uri->get("user-info")->str() == "duke:ofprunes");
    assert(uri->get("host")->str() == "upload.wikimedia.org");
    assert(uri->get("port")->int32() == 80);
    assert(uri->get("path")->str() == "/wikipedia/commons/3/33/House_sparrowIII.jpg");
    assert(uri->get("query")->str() == "query");
    assert(uri->get("fragment")->str() == "fragment");

    // version
    assert(http_header->get("version")->str() == "HTTP/1.1");
    assert(http_header->get("version-major")->int32() == 1);
    assert(http_header->get("version-minor")->int32() == 1);

    // headers
    std::vector<Octets> expected_headers;
    split(expected_headers, request, "\r\n");
    expected_headers.erase(expected_headers.begin());

    VariantSet *headers = http_header->get("headers")->variant_set();
    for(int i = 0; i < headers->array_size(); ++i)
    {
        VariantSet *header = headers->get(i)->variant_set();
        assert(header->get("header")->octets() == expected_headers[i]);
    }
    delete http_header;
}

void test2()
{
    Octets response;
    join(response, "\r\n",
        "HTTP/1.0 200 OK",
        "Server: Sun-Java-System-Web-Server/7.0",
        "Date: Thu, 18 Jun 2009 05:39:48 GMT",
        "Content-Type: image/jpeg",
        "Last-Modified: Thu, 22 Mar 2007 07:06:38 GMT",
        "Content-Length: 226651",
        "ETag: \"3755b-46022afe\"",
        "Accept-Ranges: bytes",
        "X-Cache: MISS from sq11.wikimedia.org",
        "X-Cache-Lookup: HIT from sq11.wikimedia.org:3128",
        "X-Cache: MISS from sq13.wikimedia.org",
        "X-Cache-Lookup: MISS from sq13.wikimedia.org:80",
        "Via: 1.1 sq11.wikimedia.org:3128 (squid/2.7.STABLE6), 1.0 sq13.wikimedia.org:80 (squid/2.7.STABLE6)",
        "Connection: keep-alive",
        "",
        "",
        NULL);
 
    Octets reminder;
    HttpHeaderVariantSet header_collector;
    HttpParser parser(&header_collector);
    bool response_complete = parser.parse_response(response, reminder);
    assert(response_complete == true);

    VariantSet *http_header = header_collector.release_header();

    // version
    assert(http_header->get("version")->str() == "HTTP/1.0");
    assert(http_header->get("version-major")->int32() == 1);
    assert(http_header->get("version-minor")->int32() == 0);

    // status and reason
    assert(http_header->get("status")->int32() == 200);
    assert(http_header->get("reason")->str() == "OK");
}

void test3()
{
    Octets request;
    join(request, "\r\n",
        "GET / HTTP/1.1",
        "User-Agent: bla",
        "host: localhost",
        "",
        "",
        NULL);
           
    Octets reminder;
    HttpHeaderVariantSet header_collector;
    HttpParser parser(&header_collector);
    bool request_complete = parser.parse_request(request, reminder);
    assert(request_complete == true);
}

void test4()
{
    Octets request;
    join(request, "\r\n",
        "GET / HTTP/1.1",
        "User-Agent: my agent",
        "host: localhost:80",
        "ContEnt-Length: 327",
        "Transfer-EncOding: chunked",
        "",
        "",
        NULL);
           
    Octets reminder;
    HttpHeaderVariantSet header_collector;
    HttpParser parser(&header_collector);
    bool request_complete = parser.parse_request(request, reminder);
    assert(request_complete == true);

    const std::string *content_length = 
        header_collector.header("Content-Length", std::nothrow);
    assert(content_length && *content_length == "327");

    const std::string *transfer_encoding = 
        header_collector.header("Transfer-Encoding", std::nothrow);
    assert(transfer_encoding && *transfer_encoding == "chunked");
}

void test5()
{
}

void test6()
{
}

void test7()
{
}

void test8()
{
}

std::string long_request_line()
{
    std::string request_line("GET http://host:90/");
    request_line.insert(request_line.end(), 64 * 1024, 'A');
    request_line.insert(request_line.size(), " HTTP/1.1");
    return request_line;
}

void test9()
{
    // long uri
    Octets request;
    join(request, "\r\n",
        long_request_line().c_str(),
        "User-Agent: bla",
        "host: localhost",
        "",
        "",
        NULL);
           
    Octets reminder;
    HttpHeaderVariantSet header_collector;
    HttpParser parser(&header_collector);
    bool request_complete = parser.parse_request(request, reminder);
    assert(request_complete);
}

void test10()
{
    // no uri
    Octets request;
    join(request, "\r\n",
        "GET / HTTP/1.1",
        "User-Agent: bla",
        "host: localhost",
        "",
        "",
        NULL);
           
    Octets reminder;
    HttpHeaderVariantSet header_collector;
    HttpParser parser(&header_collector);
    bool request_complete = parser.parse_request(request, reminder);
    assert(request_complete);
    VariantSet *http_header = header_collector.release_header();
    assert(http_header->get("request-uri")->variant_set()->get("path")->str() == "/");
    delete http_header;
}

void test11()
{
    std::vector<Octets> requests;
    Octets request;
    requests.push_back(
        join(request, "\r\n",
            "GET HTTP/1.1",
            "User-Agent: bla",
            "host: localhost",
            "",
            "",
            NULL));

    requests.push_back(
        join(request, "\r\n",
            " / HTTP/1.1",
            "User-Agent: bla",
            "host: localhost",
            "",
            "",
            NULL));

    requests.push_back(
        join(request, "\r\n",
            "GET / TTP/1.1",
            "User-Agent: bla",
            "host: localhost",
            "",
            "",
            NULL));

    requests.push_back(
        join(request, "\r\n",
            "GET / HTTP/1.",
            "User-Agent: bla",
            "host: localhost",
            "",
            "",
            NULL));

    requests.push_back(
        join(request, "\r\n",
            "GET / HTTP/.1",
            "User-Agent: bla",
            "host: localhost",
            "",
            "",
            NULL));

    requests.push_back(
        join(request, "\r\n",
            "GET / HTTP/11",
            "User-Agent: bla",
            "host: localhost",
            "",
            "",
            NULL));
    requests.push_back(
        join(request, "\r\n",
            "GET http HTTP/1.1",
            "User-Agent: bla",
            "host: localhost",
            "",
            "",
            NULL));
 
    requests.push_back(
        join(request, "\r\n",
            "GET http: HTTP/1.1",
            "User-Agent: bla",
            "host: localhost",
            "",
            "",
            NULL));
      
    requests.push_back(
        join(request, "\r\n",
            "GET http:/ HTTP/1.1",
            "User-Agent: bla",
            "host: localhost",
            "",
            "",
            NULL));
    
    requests.push_back(
        join(request, "\r\n",
            "GET http:// HTTP/1.1",
            "User-Agent: bla",
            "host: localhost",
            "",
            "",
            NULL));
    
    requests.push_back(
        join(request, "\r\n",
            "GET http://a HTTP/1.1",
            "User-Agent: bla",
            "host: localhost",
            "",
            "",
            NULL));
 
    requests.push_back(
        join(request, "\r\n",
            "GET http://a: HTTP/1.1",
            "User-Agent: bla",
            "host: localhost",
            "",
            "",
            NULL));
   
/*   
*/
    std::vector<Octets>::const_iterator current;
    for(current = requests.begin(); current != requests.end(); ++current)
    {
        Octets reminder;
        HttpHeaderVariantSet header_collector;
        HttpParser parser(&header_collector);
        assert_throws(std::runtime_error, parser.parse_request(request, reminder));
    }
}

int main(int argc, char **argv)
{
    test();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7();
    test8();
    test9();
    test10();
    test11();
    return 0;
}

